(function(win) {
    let selectDone = null; // 存储日期选项完成之后执行的方法
    let marks = null; //  存储标记数据
    let btnClass = "";
    let bodyClick = null; //


    // 显示日历的HTML
    const strCalendarHtml = `
        <div class="calendar-year">
            <span class="calendar-year-btn-prev"><</span>
            <span class="calendar-year-show"></span>
            <span class="calendar-year-btn-next">></span>
        </div>
        <div class="calendar-week">
            <span>日</span>
            <span>一</span>
            <span>二</span>
            <span>三</span>
            <span>四</span>
            <span>五</span>
            <span>六</span>
        </div>
        <div class="calendar-day">
            <div class="calendar-day-line">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
            <div class="calendar-day-line">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
            <div class="calendar-day-line">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
            <div class="calendar-day-line">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
            <div class="calendar-day-line">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
            <div class="calendar-day-line">
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </div>
        </div>
    `;

    // body事件处理方法

    // 隐藏并清除日历节点
    const closeCalendar = () => {
        const elCalendar = document.getElementById("layer-calendar");
        elCalendar && elCalendar.remove();
        // 执行完毕后 清空方法
        selectDone = null;
        marks = null;
        document.querySelector("body").removeEventListener("click", bodyClick, false);
    };

    // 格式化月份和日数 1 -> 01  10 => 10
    const formatMonthDay = value => value >= 10 ? value : `0${value}`;

    // 根据当前显示的年月字符串生成，年月数组
    const getArrYearMonth = () => document.querySelector(".calendar-year-show").innerText.split('-').map(item => parseInt(item));

    // 获取当前日期字符串
    const getYearMonth = d => `${d.getFullYear()}-${formatMonthDay(d.getMonth() + 1)}`

    // 根据年与月获取当前有多少天
    const getHasDays = (year, month) => {
        let arrDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        // 判断是否为润年
        if((year % 5 === 0 && year %100 !== 0) || year % 400 === 0) {
            arrDays[1] = 29;
        }
        return arrDays[month - 1];
     }

    // 获取当月第一天是星期几
    const getWeekdayByYM = (year, month) => new Date(`${year}-${month}-01`).getDay() % 7;

    // 判断是否需要添加活动数目的标记
    const isAddMark = (k) => {
        if(marks.hasOwnProperty(k)) {
            return marks[k];
        }
        return false;
    }

    // 更新每一行(第一周)的天数
    const updateLineInEle = (el, startIndex, startNumber, days) => {
        const arrYM = getArrYearMonth();
        let d = startNumber;
        [...el.children].forEach((e, i) => {
            e.className = "";
            if(i >= startIndex && d <= days) {
                // 判断是否需要添加活动数目的标记
                let strData = `${arrYM[0]}-${formatMonthDay(arrYM[1])}-${formatMonthDay(d)}`;
                let markCount = isAddMark(strData);                   
                if(markCount) {
                    // e.innerHTML = `<span class="usable mark" data-value="${strData}">${d}<i>${markCount}</i></span>` 
                    e.innerHTML = `${d}<i style='color:red;'>${markCount}</i>`;
                    e.classList.add("mark");
                } else {
                    // e.innerHTML = `<span class="usable" data-value="${arrYM[0]}-${arrYM[1]}-${d}">${d}</span>`;
                    e.innerHTML = `${d}`;
                }
                e.classList.add("usable");
                d++;
            } else {
                e.innerHTML = `&nbsp;`;
            }
        });
        return d;
    }

    // 开始更新天数列表
    const updateDaysInEle = (days, firstDayWeek) => {
        const elDaysLine = document.querySelectorAll(".calendar-day-line");
        let d = 1;
        elDaysLine.forEach((line, index) =>
            index === 0 ? d = updateLineInEle(line, firstDayWeek, d, days) : d = updateLineInEle(line, 0, d, days)
        );
    }

    // 根据年月更新日期天数的显示
    const updateDaysList = strDate => {
        const arrYM = strDate.split('-').map(item => parseInt(item));
        const days = getHasDays(arrYM[0], arrYM[1]); //当月的天数
        const firstDayWeek = getWeekdayByYM(arrYM[0], arrYM[1]); // 当前第一天是周几
        // 在元素上更新显示天数
        updateDaysInEle(days, firstDayWeek);
    }

    // 设置当前年-月
    const setYearMonth = (strDate) => {
        document.querySelector(".calendar-year-show").innerText = strDate;
        // 根据设置的年月更新日期数据
        updateDaysList(strDate);
    };

    // 切换年月
    const toggleYearMonth = n => {
        let arrYM = getArrYearMonth();
        console.log(n);
        arrYM[1] += n;
        // 如果计算后的月份超出12月
        if(arrYM[1] > 12) {
            arrYM[0] += 1;
            arrYM[1] = 1;
        }
        // 如想计算后的月份小于1
        if(arrYM[1] < 1) {
            arrYM[0] -= 1;
            arrYM[1] = 12;
        }
        setYearMonth(`${arrYM[0]}-${formatMonthDay(arrYM[1])}`);
    }

    // 开始处理日期选择
    const selectDays = e => {
        selectDone && e.target.classList.contains("mark") && selectDone(e.target.dataset.value);
        closeCalendar();
    }

    // 判断该点击的节点是否为日历部分(判断节点或祖、父辈节点的ID是否为layer-calendar)
    const isCalendarLayer = (e, k, v) => {
        if(e[k] === v)
            return true;
        if(e.parentElement)
            return isCalendarLayer(e.parentElement, k, v);
        return false;
    }

    // 绑定事件
    const bindEv = () => {
        bodyClick = e => {
            if(!isCalendarLayer(e.target, "className", btnClass) && !isCalendarLayer(e.target, "id", "layer-calendar")) {
               // closeCalendar()
            }
            // 上一个月事件
            if(e.target.classList.contains("calendar-year-btn-prev")) {
                toggleYearMonth(-1)
            }
            // 下一月事件
            if(e.target.classList.contains("calendar-year-btn-next")) {
                toggleYearMonth(1)
            }
            // 有活动的日期选择事件
            if(e.target.classList.contains("usable")) {
                selectDays(e);
            }
        };
        document.querySelector("body").addEventListener("click", bodyClick)
    }
    // 给指定元素修改或添加CSS属性
    const setCss = (el, options) => {
        for(let k in options) {
            el.style[k] = options[k];
        }
    }

    // 将日历插入页面
    const insertCalendarToPage = elP => {
        // 创建日历节点
        const elCalendar = document.createElement("div");
        elCalendar.id = "layer-calendar";
        elCalendar.innerHTML = strCalendarHtml;
        setCss(elCalendar, {
            position: "absolute",
            left: "0",
            top: "0.45rem"
        });
        setCss(elP, {
            position: "relative"
        });
        elP.appendChild(elCalendar);
    }

    // 创建日历界面并绑定事件
    const createCalendar = elP => {
        // 添加日历节点到页面
        insertCalendarToPage(elP);
        // 初次打开显示当前月份
        setYearMonth(getYearMonth(new Date()));
        // 开始绑定各种事件
        bindEv();
    }

    /*
        向外暴露接口: 在父节点中下方创建日历
        options :
            el: 展示日历的父级节点: 元素节点
            selectDone: 选择日期后执行的方法: 方法
            marks: 需标记的日程活动信息(对象): {"2021-01-02": 2, "2021-01-02": 3}
    */
    win.showCalendar = options => {
        // 如果页面已存在日历就啥都不用处理
        if(document.getElementById("layer-calendar")) {
            closeCalendar();
            return  false;
        }
        bodyClick = null;

        if(!options.hasOwnProperty("elParent")) {
            console.log('未指定日历的父节点!: elParent');
            return false;
        }

        if(!options.hasOwnProperty("btnClass")) {
            console.log('未指定触发日历显示的元素的className!： btnClass');
            return false;
        }

        // 存储日期选择完毕后的执行方法
        selectDone = options.hasOwnProperty('selectDone') ? options.selectDone : null;
        // 存储日历标记
        marks = options.hasOwnProperty("marks") ? options.marks : null;
        // 存储触发日历的元素的className
        btnClass = options.hasOwnProperty("btnClass") ? options.btnClass : null;
        // 开始创建日历界面
        createCalendar(options.elParent);
    }

})(window);